#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>

int fd;              // 文件描述符
int ret;             // 返回值
char gpio_path[100]; // GPIO路径
int len;             // 字符串长度
char file_path[100]; // 文件路径
char buf[2];         // 缓冲区

struct pollfd fds[1]; // poll结构体数组

// 导出GPIO引脚
int gpio_export(char *argv)
{
    fd = open("/sys/class/gpio/export", O_WRONLY); // 打开export文件
    if (fd < 0)
    {
        printf("open /sys/class/gpio/export error \n"); // 打开文件失败
        return -1;
    }
    len = strlen(argv);         // 获取字符串长度
    ret = write(fd, argv, len); // 写入引脚号到export文件
    if (ret < 0)
    {
        printf("write /sys/class/gpio/export error \n"); // 写入失败
        return -2;
    }
    close(fd); // 关闭文件
}

// 取消导出GPIO引脚
int gpio_unexport(char *argv)
{
    fd = open("/sys/class/gpio/unexport", O_WRONLY); // 打开unexport文件
    if (fd < 0)
    {
        printf("open /sys/class/gpio/unexport error \n"); // 打开文件失败
        return -1;
    }
    len = strlen(argv);        // 获取字符串长度
    ret = write(fd, argv, len); // 写入引脚号到unexport文件
    if (ret < 0)
    {
        printf("write /sys/class/gpio/unexport error \n"); // 写入失败
        return -2;
    }
    close(fd); // 关闭文件
}

// 控制GPIO引脚的属性
int gpio_ctrl(char *arg, char *val)
{
    sprintf(file_path, "%s/%s", gpio_path, arg); // 构建属性文件的路径
    fd = open(file_path, O_WRONLY);              // 打开属性文件
    if (fd < 0)
    {
        printf("open file_path error \n"); // 打开文件失败
        return -1;
    }
    len = strlen(val);         // 获取字符串长度
    ret = write(fd, val, len); // 写入属性值到属性文件
    if (ret < 0)
    {
        printf("write file_path error\n"); // 写入失败
        return -2;
    }
    close(fd); // 关闭文件
}

// 监听GPIO引脚的中断事件
int gpio_interrupt(char *arg)
{
    sprintf(file_path, "%s/%s", gpio_path, arg); // 构建文件路径
    fd = open(file_path, O_RDONLY);              // 打开文件
    if (fd < 0)
    {
        printf("open file_path error \n"); // 打开文件失败
        return -1;
    }
    memset((void *)fds, 0, sizeof(fds)); // 清空poll结构体数组
    fds[0].fd = fd;                      // 设置poll结构体的文件描述符
    fds[0].events = POLLPRI;             // 设置poll结构体的事件类型为POLLPRI，表示有紧急数据可读

    read(fd, buf, 2); // 读取文件内容，清除中断事件

    ret = poll(fds, 1, -1); // 调用poll函数等待中断事件发生，阻塞直到事件发生
    if (ret <= 0)
    {
        printf("poll error \n"); // 调用poll失败或超时
        return -1;
    }
    if(fds[0].revents & POLLPRI)
    {
        lseek(fd, 0, SEEK_SET); // 重新定位文件指针到文件开头
        read(fd, buf, 2);       // 读取文件内容，获取中断事件的值
        buf[1] = '\0';
        printf("value is %s\n", buf); // 输出中断事件的值
    }
}

// 读取GPIO引脚的值
int gpio_read_value(char *arg)
{
    sprintf(file_path, "%s/%s", gpio_path, arg); // 构建文件路径
   fd = open(file_path, O_WRONLY); // 打开文件，以只写模式打开是一个错误，应该使用只读模式
    if (fd < 0)
    {
        printf("open file_path error\n"); // 打开文件失败
        return -1;
    }
    ret = read(fd, buf, 1); // 读取文件内容，获取引脚的值
    if (!strcmp(buf, "1"))
    {
        printf("The value is high\n"); // 引脚值为高电平
        return 1;
    }
    else if (!strcmp(buf, "0"))
    {
        printf("The value is low\n"); // 引脚值为低电平
        return 0;
    }
    return -1; // 这里应该返回读取到的引脚值（0或1），而不是返回固定的-1
    close(fd); // 关闭文件（这行代码无法执行到，应该放在read之前）
}

int main(int argc, char *argv[]) // 主函数
{
    int value;
    sprintf(gpio_path, "/sys/class/gpio/gpio%s", argv[1]); // 构建GPIO路径
    if (access(gpio_path, F_OK))                            // 检查GPIO路径是否存在
    {
        gpio_export(argv[1]); // 不存在则导出GPIO引脚
    }
    else
    {
        gpio_unexport(argv[1]); // 存在则取消导出GPIO引脚
    }

    gpio_ctrl("direction", "in"); // 设置GPIO引脚为输入模式
    gpio_ctrl("edge", "both");    // 设置GPIO引脚的中断触发方式为上升沿和下降沿
    gpio_interrupt("value");      // 监听GPIO引脚的中断事件

    gpio_unexport(argv[1]); // 最后取消导出GPIO引脚

    return 0; // 返回0表示程序正常退出
}